
#include "DbgUtil.h"
#include "FileUtil.h"
#include "SysUtil.h"

using namespace x2lib;

const char DbgUtil::m_strLvl[6][16] = { {"INFO"}, {"DEBUG"}, {"WARN"}, {"ERROR"}, {"FATAL"}, {"TEXT"} };

bool enumFilesProc(bool isFile, const char* filePath, uint64_t fileLen, void* p)
{
	if(isFile)
	{
		// uint32_t uYmd = *(uint32_t*)p;
		// int index = FileUtil::FindLastSlash(filePath);
		// char* pszFile = (char*)&filePath[index + 1];
		// char* p0 = strchr(pszFile, '-');
		// char* p1 = strrchr(pszFile, '-');
		// if (p0 && p1)
		// {
		// 	*p1 = 0;
		// 	if (atoi(p0 + 1) <= uYmd)
		// 	{
		// 		FileUtil::DeleteDirFiles(filePath, false);
		// 	}
		// }
		return true;
	}
	return false;
}

DbgUtil::DbgUtil(const char* pszDir, uint8_t nExpDays, uint8_t eLevel, const char arrFilter[5][64], uint8_t uOutBits, void(*callback)(const char*))
{
	m_pLogBuf = (char*)malloc(BUFF_SIZE);

	m_eLevel = (ELevel)eLevel;
	m_pCallback = callback;
	m_ignoreFilter = (arrFilter == nullptr || arrFilter[0][0] == 0);
	if (arrFilter)
	{
		memcpy((void*)m_arrFilter, (void*)arrFilter, sizeof(m_arrFilter));
	}
	m_uOutBits = (EOutBit)uOutBits;

	if (pszDir && pszDir[0])
	{
		strcpy(m_szOutDir, pszDir);
	}
	else
	{
		strcpy(m_szOutDir, SysUtil::GetWorkDir(1));
	}

	int len = strlen(m_szOutDir) - 1;
	if (m_szOutDir[len] != '\\' && m_szOutDir[len] != '/')
	{
		m_szOutDir[len + 1] = '/';
	}

	const char* pszProcName = SysUtil::GetWorkDir(2);
	char strDateTime[64];
	sprintf(m_szFile, "%s%s-%s.txt", m_szOutDir, pszProcName, SysUtil::GetFmtDateTime(strDateTime, sizeof(strDateTime), 0, "%Y%m%d-%H%M%S"));

	// 尝试删除过期日志
	uint32_t tmBefore = SysUtil::GetDateTimeTick() - nExpDays * 24 * 60 * 60;
	SysUtil::GetFmtDateTime(strDateTime, sizeof(strDateTime), tmBefore, "%Y%m%d");
	uint32_t uYmd = atoi(strDateTime);
	char szWilds[512] = { 0 };
	sprintf(szWilds, "%s-*-*.txt", pszProcName);
	FileUtil::EnumFiles(m_szOutDir, 1, enumFilesProc, &uYmd);
}

DbgUtil::~DbgUtil()
{
	free(m_pLogBuf);
}

bool DbgUtil::initInst()
{
	return FileUtil::MakeDirs(m_szOutDir);
}

void DbgUtil::unInitInst()
{
}

void DbgUtil::Out(ELevel eLevel, const char* pszFile, int nLine, const char* pszFmt, ...)
{
	m_mutex.Lock();

	do
	{
		// 过滤级别
		if (!testLevel(eLevel)) { break; }

		char strDate[64];
		SysUtil::GetFmtDateTime(strDate, sizeof(strDate), 0, "%m-%d %H:%M:%S");
		int index = FileUtil::FindLastSlash(pszFile);
		const char* pszFileName = &pszFile[index + 1];

		uint32_t tid = xCores::Engine::GetCurId();
		int nChars = sprintf(m_pLogBuf, "[%s][%s,%s,Tid:%06d,L:%05d]", m_strLvl[eLevel], strDate, pszFileName, tid, nLine);
		if (nChars < 0) { break; }

		va_list body;
		va_start(body, pszFmt);
		vsnprintf(m_pLogBuf + nChars, BUFF_SIZE - nChars, pszFmt, body);
		va_end(body);

		// 过滤关键词
		if (!testFilter(m_pLogBuf + nChars)) { break; }


#ifdef __X2LIB_WIN32__
		OutputDebugStringA(m_pLogBuf);
#endif

		if (m_uOutBits & OutBit_File)
		{
			FILE* fp = fopen(m_szFile, "at+");
			if (fp != nullptr)
			{
				fprintf(fp, m_pLogBuf);
				fclose(fp);
			}
		}

		if (m_uOutBits & OutBit_Console)
		{
			printf(m_pLogBuf); // 遇到过阻塞，可能是锁控制不佳
		}

		if ((m_uOutBits & OutBit_Callback) && m_pCallback)
		{
			m_pCallback(m_pLogBuf);
		}

	} while (false);

	m_mutex.Unlock();
}

void DbgUtil::Box(ELevel eLevel, int nLine, const char* pszFmt, ...)
{
#ifdef __X2LIB_WIN32__
	char out[1024];
	va_list body;
	va_start(body, pszFmt);
	vsnprintf(out, sizeof(out), pszFmt, body);
	va_end(body);

	MessageBoxA(NULL, out, "MsgBox", 0);
#endif
}

// szOutFile - only file name, no path
void DbgUtil::Bin(ELevel eLevel, const char* pszFile, int nLine, const char* pszBinName, const char* pData, uint32_t nData)
{
	char szFile[512];
	sprintf(szFile, "%s\\%s", m_szOutDir, pszBinName);
	bool bRet = FileUtil::ReadWrite(szFile, (void*)pData, nData, false);
	Out(eLevel, szFile, nLine, "Output Bin[%d]: %s, nData=%d\n", bRet, szFile, nData);
}

bool DbgUtil::testLevel(ELevel eLevel)
{
	return (eLevel >= m_eLevel);
}

bool DbgUtil::testFilter(const char * pszLog)
{
	if (m_ignoreFilter) { return true; }

	for (int i = 0; i < sizeof(m_arrFilter) / sizeof(m_arrFilter[0]); ++i)
	{
		if (m_arrFilter[i][0] && strstr(pszLog, m_arrFilter[i]))
			return true;
	}
	return false;
}

